package com.rayframework.context;

import com.ray.tool.RayConsole;
import com.ray.tool.RayStringTools;
import com.ray.tool.file.RayFileTools;

import com.rayframework.context.common.constant.RayContextConstants;
import com.rayframework.context.utils.ApplicationConfigUtils;
import com.rayframework.context.utils.RayConsoleUtils;
import com.rayframework.context.utils.RayContextUtils;
import com.rayframework.core.common.constant.RayCoreConstants;
import org.springframework.core.env.*;
import org.springframework.lang.NonNull;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 环境配置 - 核心之一
 *
 * @author LiaoYuXing-Ray
 * @version 1.0
 * @createDate 2023/11/26 15:47
 **/
public class RayApplicationContext {
    /**
     * 初始化标记 2023/9/12 15:31
     */
    private static boolean isInitialized = false;

    public static boolean getIsIsInitialized() {
        return isInitialized;
    }

    /**
     * 全局环境配置类对象 - 只读 2023/9/7 14:41
     * 通常不建议直接修改ConfigurableEnvironment环境内的配置，而是通过注入其他Bean或者使用其他方式来实现对配置的修改。这是因为直接修改ConfigurableEnvironment可能会导致不可预期的行为，例如与其他Bean产生冲突、破坏应用程序的状态等。
     */
    private static ConfigurableEnvironment environment;

    /**
     * 存储属性值 2023/9/14 10:17
     * 缓存了部分可动态配置的属性，一定要和 environment 配置同步，不然程序将可能会出现灾难性故障
     */
    public static Map<String, Object> applicationConfigMap;

    /**
     * 框架启动环境准备
     *
     * @author LiaoYuXing-Ray 2023/9/7 11:44
     **/
    public static void init() {
        if (!isInitialized) {
            // 必须第1个加载，框架启动环境准备
            prepareEnvironment();
            // 标记已经初始化
            isInitialized = true;
        }
    }


    /**
     * 获取环境中配置key对应的value值 - 重载 【慎用】<br/>
     * 带有此 canUpdate 的配置文件内的相应属性可被修改，否则使用只读配置{@link RayApplicationContext#environment}
     *
     * @param canUpdate   是否可更新
     * @param propertyKey 配置文件内的属性key
     * @return java.lang.String 环境中配置key对应的value值
     * @author LiaoYuXing-Ray 2023/9/7 16:39
     **/
    public static String getApplicationPropertyValue(@NonNull boolean canUpdate, String propertyKey) {
        if (canUpdate) {
            return (String) applicationConfigMap.get(propertyKey);
        } else {
            return getApplicationPropertyValue(propertyKey);
        }
    }

    /**
     * 获取环境中配置key对应的value值 【常用】
     *
     * @param propertyKey 配置文件内的属性key
     * @return java.lang.String 环境中配置key对应的value值
     * @author LiaoYuXing-Ray 2023/9/7 16:39
     **/
    public static String getApplicationPropertyValue(String propertyKey) {
        return environment.getProperty(propertyKey);
    }

    /**
     * 修改缓存中环境配置key对应的value值 - 【慎用】
     * 此处只能支持部分框架自定义可修改的配置，如打印框架日志的方式，颜色
     * 不能改的key的value被改了也不会被影响，因为不能改的key使用的接口不同
     *
     * @param propertyKey 配置文件内的属性key
     * @param newValue    环境中配置key的新value值
     * @return boolean 是否修改成功
     * @author LiaoYuXing-Ray 2023/9/14 10:41
     **/
    public synchronized static boolean updateApplicationConfigMapValue(String propertyKey, Object newValue) {
        boolean result = false;
        boolean canUpdate = false;
        for (String s : RayContextConstants.CAN_UPDATE_APPLICATION_PROPERTY_KEY) {
            if (s.contains(propertyKey)) {
                canUpdate = true;
                break;
            }
        }
        if (!canUpdate) {
            RayConsoleUtils.err("修改全局环境配置失败！[" + propertyKey + "]为加载后禁止修改的配置项");
            return false;
        }

        // 需要初始化完
        if (getIsIsInitialized()) {
            if (applicationConfigMap.containsKey(propertyKey)) {
                String oldValue = (String) applicationConfigMap.get(propertyKey);
                applicationConfigMap.put(propertyKey, newValue);
                RayConsoleUtils.out("修改全局环境配置：["+propertyKey+"]旧值：["+oldValue+"]新值：["+applicationConfigMap.get(propertyKey)+ "]");
                result = true;
            }
        }
        return result;
    }

    /**
     * 获取 全局环境配置类对象 中的资源配置 - 公有
     *
     * @param environment 全局环境配置类对象
     * @param sourcesKey  资源配置的key
     * @return org.springframework.core.env.PropertySource<?>
     * @author LiaoYuXing-Ray 2023/9/14 10:21
     **/
    public static PropertySource<?> getPropertySource(ConfigurableEnvironment environment, String sourcesKey) {
        MutablePropertySources propertySources = environment.getPropertySources();
        return propertySources.get(sourcesKey);
    }


    /**
     * 获取 全局环境配置类对象 中的资源配置 - 重载 私有默认获取 {@link RayCoreConstants#APPLICATION}
     *
     * @param environment 全局环境配置类对象
     * @author LiaoYuXing-Ray 2023/9/14 10:21
     **/
    private static PropertySource<?> getPropertySource(ConfigurableEnvironment environment) {
        return getPropertySource(environment, RayCoreConstants.APPLICATION);
    }

    /**
     * 获取 全局环境配置类对象 中的资源配置 - 重载 私有默认获取 {@link RayCoreConstants#APPLICATION}
     *
     * @author LiaoYuXing-Ray 2023/9/14 10:21
     */
    private static PropertySource<?> getPropertySource() {
        return getPropertySource(environment);
    }

    /**
     * 获取环境中的所有配置
     *
     * @return java.util.Map<java.lang.String, java.lang.Object> 返回配置Map
     * @author LiaoYuXing-Ray 2023/9/14 9:25
     */
    public static Map<String, Object> getAllApplicationConfigurationsMap() {
        if (getIsIsInitialized()) {
            Map<String, Object> configurations = new LinkedHashMap<>();
            PropertySource<?> propertySource = getPropertySource();
            if (propertySource instanceof MapPropertySource) {
                MapPropertySource mapPropertySource = (MapPropertySource) propertySource;
                for (String key : mapPropertySource.getPropertyNames()) {
                    configurations.put(key, mapPropertySource.getProperty(key));
                }
            }
            return configurations;
        }
        return null;
    }


    /**
     * 加载环境配置 TODO bug : Map加载会有异常，不能少，这就不行
     * <p>
     * 先加载系统默认的配置文件，再加载用户的配置文件
     * 配置加载流程中，各种配置属性会封装成一个个抽象的数据结构 PropertySource 中。
     * 数据结构代码格式key-value形式
     *
     * @author LiaoYuXing-Ray 2023/9/7 14:40
     **/
    public static void prepareEnvironment() {
        try {
            if (environment == null) {
                // 创建一个ConfigurableEnvironment对象，并设置MutablePropertySources
                environment = new StandardEnvironment();
                // 创建一个空的MutablePropertySources对象
                MutablePropertySources propertySources = new MutablePropertySources();
                // 存储属性值
                applicationConfigMap = new HashMap<>();
                // 创建Properties对象
                Properties propertiesApp = new Properties();

                /*
                 * 先加载框架默认的配置
                 * 通过jar包
                 * 2023/9/7 17:10
                 */
                String jarPath = RayContextUtils.getRayJar();
                if (RayStringTools.isNotBlank(jarPath)) {
                    try (JarFile jarFile = new JarFile(jarPath)) {
                        // 枚举jar包中的所有条目
                        Enumeration<JarEntry> entries = jarFile.entries();
                        while (entries.hasMoreElements()) {
                            JarEntry entry = entries.nextElement();
                            String name = entry.getName();
                            if (name.endsWith(RayCoreConstants.APPLICATION_PROPERTIES)) { // 只读取以"APPLICATION_PROPERTIES"结尾的文件
                                try (InputStream inputStream = jarFile.getInputStream(entry)) {
                                    // 加载配置文件
                                    propertiesApp.load(inputStream);
                                    // 遍历属性并添加到Map中
                                    for (String key : propertiesApp.stringPropertyNames()) {
                                        String value = propertiesApp.getProperty(key);
                                        applicationConfigMap.put(key, value);
                                    }
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    } catch (IOException e) {
                        System.out.println("Error opening jar file: " + jarPath);
                        e.printStackTrace();
                    }
                }


                /*
                 * 再加载用户的配置，相同key替换，这样用户可以自定义修改和增加配置项
                 * 通过RayBoot.getApplicationPropertyValue(String key)获取value
                 * 2023/9/7 17:10
                 */
                // 创建Properties对象
                Properties propertiesUser = new Properties();
                try (FileInputStream fis = new FileInputStream(RayFileTools.pathCombine(1,
                        ApplicationConfigUtils.getAppRoot()
                        , "src", "main", "resources", RayCoreConstants.APPLICATION_PROPERTIES))) {
                    // 加载配置文件
                    propertiesUser.load(fis);
                    // 遍历属性并添加到Map中
                    for (String key : propertiesUser.stringPropertyNames()) {
                        String value = propertiesUser.getProperty(key);
                        applicationConfigMap.put(key, value);
                    }
                } catch (IOException e) {
                    RayConsole.err("读取配置文件[" + RayCoreConstants.APPLICATION_PROPERTIES + "]失败，启用框架默认配置！" + e.getMessage());
                }

                /*
                 * 加载资源 2023/9/12 13:30
                 */
                MapPropertySource mapPropertySource = new MapPropertySource(RayCoreConstants.APPLICATION, applicationConfigMap);
                propertySources.addFirst(mapPropertySource);

                PropertySource<?> application = propertySources.get(RayCoreConstants.APPLICATION);// 获取名为"application"的PropertySource对象
                if (application != null) {
                    // 现在，environment对象已经配置了属性源，可以通过environment.getProperty()方法获取属性值
                    environment.getPropertySources().addFirst(application);
                }
            }

        } catch (Exception e) {
            RayConsole.err("建议重启程序！环境配置加载异常，部分功能可能发生灾难性错误！" + e.getMessage());
        }
    }


}
